
/**
 * 
 */
package com.vision.sales.cm.db.data;

import java.io.Serializable;
import java.sql.Timestamp;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

import net.sf.gilead.pojo.gwt.LightEntity;

import com.vision.core.cm.data.IHasSystemTime;
import com.vision.core.cm.db.data.Company;
import com.vision.core.cm.db.data.CompanyGroup;
import com.vision.sales.cm.data.CollectionStatus;
import com.vision.sales.cm.data.IHasTransactionStatus;
import com.vision.sales.cm.data.TransactionStatus;


/**
 * @author Mark
 *
 */
@Entity
@Table(name = SalesInvoice.TABLE)
public class SalesInvoice extends LightEntity implements Serializable, IHasTransactionStatus, IHasSystemTime {
	
	public static interface FieldSpecs {
		int INVOICE_NO_MAX = 20;
		int PO_NO_MAX = 30;
		int ITEMS_DELIVERED_MAX = 300;
		int REMARKS_MAX = 300;
	}
	
	private static final long serialVersionUID = -5379665424881702625L;
	
	// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	public static final String TABLE = "sales_invoice";
	
	public static final String COL_COMPANY_ID = "company_id";
	public static final String COL_ID = "id";
	public static final String COL_GROUP_ID = "group_id";
	public static final String COL_INV_NO = "invoice_no";
	public static final String COL_INV_DATE = "invoice_date";
	public static final String COL_PO_NO = "po_no";
	
	public static final String COL_CUSTOMER_ID = "customer_id";
	
	public static final String COL_PRODUCT_ID = "product_id";
	public static final String COL_SALESMAN_ID = "salesman_id";
	public static final String COL_COMMISSION_RATE = "commission_rate";
	
	public static final String COL_ITEMS_DELIVERED = "items_delivered";
	public static final String COL_REMARKS = "remarks";
	
	public static final String COL_GROSS_SALES = "gross_sales";
	
	public static final String COL_AMT_PAID = "amount_paid";
	public static final String COL_WTAX_PAID = "wtax_paid";

	public static final String COL_ADVANCE = "advance";
	public static final String COL_OVERRUNS = "overruns";
	public static final String COL_DISCOUNTS = "discounts";
	public static final String COL_REJECTS = "rejects";
	public static final String COL_RETURNS = "returns";
	public static final String COL_BAD_DEBTS = "bad_debts";
	public static final String COL_OVERPAYMENT = "overpayment";
	
	public static final String COL_NET_INCOME = "net_income";
	
	public static final String COL_BALANCE = "balance";
	
	public static final String COL_COLLECTION_STATUS = "collection_status";
	
	public static final String COL_STATUS = "status";
	
	public static final String COL_SYS_CREATED = "sys_created";
	public static final String COL_SYS_LAST_MOD = "sys_last_mod";
	
	// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

	/**
	 * System-generated ID of the {@link SalesInvoice}.
	 */
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO, generator="ssales_invoice")
	@SequenceGenerator(name = "ssales_invoice", sequenceName = "sales_invoice_seq")
	@Column(name = COL_ID)
	private Long id;
	
	/**
	 * The {@link Company}.
	 */
	@ManyToOne
	@JoinColumn(name = COL_COMPANY_ID, nullable = false)
	private Company company;
	
	/**
	 * The {@link CompanyGroup}.
	 */
	@ManyToOne
	@JoinColumn(name = COL_GROUP_ID, nullable = false)
	private CompanyGroup group;
	
	/**
	 * User-defined number of the {@link SalesInvoice}.
	 */
	@Column(name = COL_INV_NO, nullable = false, unique = true, updatable = false, length = FieldSpecs.INVOICE_NO_MAX)
	private String invoiceNo;
	
	/**
	 * The date the {@link SalesInvoice} was issued.
	 */
	@Column(name = COL_INV_DATE, nullable = false)
	@Temporal(TemporalType.DATE)
	private Date date;
	
	/**
	 * User-defined P.O. number for the {@link SalesInvoice}.
	 */
	@Column(name = COL_PO_NO, nullable = false, length = FieldSpecs.PO_NO_MAX)
	private String poNo;
	
	/**
	 * The {@link Customer} the {@link SalesInvoice} is issued.
	 */
	@ManyToOne
	@JoinColumn(name = COL_CUSTOMER_ID, nullable = false)
	private Customer customer;
	
	/**
	 * The {@link Product}.
	 */
	@ManyToOne
	@JoinColumn(name = COL_PRODUCT_ID, nullable = false)
	private Product product;
	
	/**
	 * The {@link Salesman}.
	 */
	@ManyToOne
	@JoinColumn(name = COL_SALESMAN_ID, nullable = false)
	private Salesman salesman;
	
	/**
	 * The commission rate for this transaction based on what is defined for the
	 * {@link #salesman} and {@link #product}.
	 */
	@Column(name = COL_COMMISSION_RATE, nullable = false)
	private double commissionRate;
	
	/**
	 * Textual data containing the items included in the {@link SalesInvoice}.
	 */
	@Column(name = COL_ITEMS_DELIVERED, nullable = true, length = FieldSpecs.ITEMS_DELIVERED_MAX)
	private String itemsDelivered;
	
	/**
	 * User remarks.
	 */
	@Column(name = COL_REMARKS, length = FieldSpecs.REMARKS_MAX)
	private String remarks;
	
	/**
	 * The {@link SalesInvoice}'s gross sales amount.
	 */
	@Column(name = COL_GROSS_SALES, nullable = false)
	private double grossSales;
	
	/**
	 * System-computed total amount paid for the {@link SalesInvoice}, based on
	 * {@link Collection}.
	 */
	@Column(name = COL_AMT_PAID, nullable = false)
	private double amountPaid;

	/**
	 * System-computed total withholding tax paid based on {@link Collection}.
	 */
	@Column(name = COL_WTAX_PAID, nullable = false)
	private double withHoldingTaxPaid;
	
	/**
	 * System-computed total amount of advance payments made, based on {@link AdvancePayment}.
	 */
	@Column(name = COL_ADVANCE, nullable = false)
	private double advance;
	
	/**
	 * System-computed total amount of overruns, based on {@link DebitMemo}.
	 */
	@Column(name = COL_OVERRUNS, nullable = false)
	private double overruns;
	
	/**
	 * System-computed total amount of discounts, based on {@link CreditMemo}.
	 */
	@Column(name = COL_DISCOUNTS, nullable = false)
	private double discounts;
	
	/**
	 * System-computed total amount of rejects, based on {@link CreditMemo}.
	 */
	@Column(name = COL_REJECTS, nullable = false)
	private double rejects;
	
	/**
	 * System-computed total amount of returns, based on {@link CreditMemo}.
	 */
	@Column(name = COL_RETURNS, nullable = false)
	private double returns;
	
	/**
	 * System-computed total amount of bad debts, based on {@link CreditMemo}.
	 */
	@Column(name = COL_BAD_DEBTS, nullable = false)
	private double badDebts;
	
	@Column(name = COL_OVERPAYMENT, nullable = false)
	private double overpayment;
	
	@Column(name = COL_NET_INCOME, nullable = false)
	private double netIncome;
	
	/**
	 * System-computed balance of the {@link SalesInvoice}.
	 */
	@Column(name = COL_BALANCE, nullable = false)
	private double balance;
	
	/**
	 * The {@link SalesInvoice}'s collection status.
	 */
	@Column(name = COL_COLLECTION_STATUS, nullable = false)
	private CollectionStatus collectionStatus;
	
	@Column(name = COL_STATUS, nullable = false)
	@Enumerated(EnumType.STRING)
	private TransactionStatus status;
	
	/**
	 * The list of {@link DeliveryReceipt}s associated to this {@link SalesInvoice}.
	 */
	// Some notes on the @OneToMany annotation:
	// 		One SalesInvoice can have many DeliveryReceipts.
	// 		The relationship is mapped by the "salesInvoice" field in DeliveryReceipt (NOTE: field name is used, not column name).
	// 		The list of DeliveryReceipts are lazily fetched.
	// 		The list of DeliveryReceipts are also saved when the SalesInvoice is persisted or merged.
	@OneToMany(mappedBy = "salesInvoice", cascade = { CascadeType.PERSIST, CascadeType.MERGE })
	private Set<DeliveryReceipt> deliveryReceipts = new HashSet<DeliveryReceipt>();
	
//	@OneToMany(mappedBy = "salesInvoice", cascade = { CascadeType.PERSIST, CascadeType.MERGE })
//	private Set<AdvancePayment> advancePayments = new HashSet<AdvancePayment>();
	
	@OneToMany(mappedBy = "salesInvoice", cascade = { CascadeType.PERSIST, CascadeType.MERGE })
	private Set<DebitMemo> debitMemos = new HashSet<DebitMemo>();
	
	@OneToMany(mappedBy = "salesInvoice", cascade = { CascadeType.PERSIST, CascadeType.MERGE })
	private Set<CreditMemo> creditMemos = new HashSet<CreditMemo>();
	
	@OneToMany(mappedBy = "salesInvoice", cascade = { CascadeType.PERSIST, CascadeType.MERGE })
	private Set<CollectionItem> collectionItems = new HashSet<CollectionItem>();
	
	// For internal use %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	/**
	 * The server {@link Timestamp} this object was created.
	 */
	@Column(name = COL_SYS_CREATED, nullable = false)
	private Timestamp sysCreated;
	
	/**
	 * The server {@link Timestamp} this object was last modified.
	 */
	@Column(name = COL_SYS_LAST_MOD, nullable = false)
	private Timestamp sysLastModified;

	// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	public SalesInvoice() { } // For Serializable
	
	public SalesInvoice(Date invoiceDate) {
		this.date = invoiceDate;
	}

	@Override
	public boolean equals(Object object) {
		if (object instanceof SalesInvoice) {
			SalesInvoice o = (SalesInvoice) object;
			return id.equals(o.id);
		}
		return false;
	}
	
	@Override
	public int hashCode() {
		return id == null ? super.hashCode() : id.hashCode();
	}
	
	@Override
	public String toString() {
		return id + ": " + invoiceNo + "(" + date + ")";
	}

	// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}
	
	public Company getCompany() {
		return company;
	}

	public void setCompany(Company company) {
		this.company = company;
	}

	public CompanyGroup getGroup() {
		return group;
	}

	public void setGroup(CompanyGroup group) {
		this.group = group;
	}

	public String getInvoiceNo() {
		return invoiceNo;
	}

	public void setInvoiceNo(String invoiceNo) {
		this.invoiceNo = invoiceNo;
	}
	
	public String getGroupAndNo() {
		return group.getCode() + "-" + invoiceNo;
	}
	
	public void setGroupAndNo(String groupAndNo) {
		// Does nothing.
	}
	
	public Date getDate() {
		return date;
	}

	public void setDate(Date date) {
		this.date = date;
	}

	public String getPoNo() {
		return poNo;
	}

	public void setPoNo(String poNo) {
		this.poNo = poNo;
	}

	public Customer getCustomer() {
		return customer;
	}

	public void setCustomer(Customer customer) {
		this.customer = customer;
	}
	
	public Long getCustomerId() {
		return getCustomer().getId();
	}

	public Product getProduct() {
		return product;
	}

	public void setProduct(Product product) {
		this.product = product;
	}
	
	public Long getProductId() {
		return getProduct().getId();
	}

	public Salesman getSalesman() {
		return salesman;
	}

	public void setSalesman(Salesman salesman) {
		this.salesman = salesman;
	}

	public double getCommissionRate() {
		return commissionRate;
	}

	public void setCommissionRate(double commissionRate) {
		this.commissionRate = commissionRate;
	}

	public String getItemsDelivered() {
		return itemsDelivered;
	}

	public void setItemsDelivered(String itemsDelivered) {
		this.itemsDelivered = itemsDelivered;
	}

	public String getRemarks() {
		return remarks;
	}

	public void setRemarks(String remarks) {
		this.remarks = remarks;
	}

	public double getGrossSales() {
		return grossSales;
	}

	public void setGrossSales(double grossSales) {
		this.grossSales = grossSales;
	}

	public double getAmountPaid() {
		return amountPaid;
	}

	public void setAmountPaid(double amountPaid) {
		this.amountPaid = amountPaid;
	}

	public double getWithHoldingTaxPaid() {
		return withHoldingTaxPaid;
	}

	public void setWithHoldingTaxPaid(double withHoldingTaxPaid) {
		this.withHoldingTaxPaid = withHoldingTaxPaid;
	}

	public double getAdvance() {
		return advance;
	}

	public void setAdvance(double advance) {
		this.advance = advance;
	}

	public double getOverruns() {
		return overruns;
	}

	public void setOverruns(double overruns) {
		this.overruns = overruns;
	}

	public double getDiscounts() {
		return discounts;
	}

	public void setDiscounts(double discounts) {
		this.discounts = discounts;
	}

	public double getRejects() {
		return rejects;
	}

	public void setRejects(double rejects) {
		this.rejects = rejects;
	}

	public double getReturns() {
		return returns;
	}

	public void setReturns(double returns) {
		this.returns = returns;
	}

	public double getBadDebts() {
		return badDebts;
	}

	public void setBadDebts(double badDebts) {
		this.badDebts = badDebts;
	}

	public double getOverpayment() {
		return overpayment;
	}

	public void setOverpayment(double overpayment) {
		this.overpayment = overpayment;
	}

	public double getNetIncome() {
		return netIncome;
	}

	public void setNetIncome(double netIncome) {
		this.netIncome = netIncome;
	}

	public double getBalance() {
		return balance;
	}

	public void setBalance(double balance) {
		this.balance = balance;
	}

	public CollectionStatus getCollectionStatus() {
		return collectionStatus;
	}

	public void setCollectionStatus(CollectionStatus collectionStatus) {
		this.collectionStatus = collectionStatus;
	}
	
	public TransactionStatus getStatus() {
		return status;
	}
	
	public void setStatus(TransactionStatus status) {
		this.status = status;
	}

	public Set<DeliveryReceipt> getDeliveryReceipts() {
		return deliveryReceipts;
	}

	public void setDeliveryReceipts(Set<DeliveryReceipt> deliveryReceipts) {
		this.deliveryReceipts = deliveryReceipts;
	}

	public Set<DebitMemo> getDebitMemos() {
		return debitMemos;
	}

	public void setDebitMemos(Set<DebitMemo> debitMemos) {
		this.debitMemos = debitMemos;
	}

	public Set<CreditMemo> getCreditMemos() {
		return creditMemos;
	}

	public void setCreditMemos(Set<CreditMemo> creditMemos) {
		this.creditMemos = creditMemos;
	}
	
	public Set<CollectionItem> getCollectionItems() {
		return collectionItems;
	}
	
	public void setCollectionItems(Set<CollectionItem> collectionItems) {
		this.collectionItems = collectionItems;
	}

	public Timestamp getSysCreated() {
		return sysCreated;
	}

	public void setSysCreated(Timestamp sysCreated) {
		this.sysCreated = sysCreated;
	}

	public Timestamp getSysLastModified() {
		return sysLastModified;
	}

	public void setSysLastModified(Timestamp sysLastModified) {
		this.sysLastModified = sysLastModified;
	}
	
}
